use std::env;
use std::fs;
use std::io::{self, Write};
use std::net::{TcpListener, TcpStream};
use std::path::PathBuf;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener as TokioTcpListener;

#[tokio::main]
async fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();

    if args.len() == 3 {
        let port = args[1].clone();
        if !port.clone().parse::<i16>().is_ok() {
            panic!("端口号必须是有效的数字！例如: ./sun-ftp-server 2221 dir_path");
        }
        let path = args[2].clone();
        let host = "192.168.1.15:".to_owned() + &port;
        let listener = TokioTcpListener::bind(host).await?;
        println!("FTP server listening on port {}", &port);

        while let Ok((stream, _)) = listener.accept().await {
            println!("Sun FTP server started");
            tokio::spawn(handle_client(stream, path.clone()));
        }
    } else {
        panic!("没有指定共享路径或者端口号！例如: ./sun-ftp-server 2221 dir_path");
    }
    Ok(())
}

async fn handle_client(mut stream: tokio::net::TcpStream, path: String) -> io::Result<()> {
    // 发送欢迎消息
    stream
        .write_all(b"220 Welcome to the Sun FTP server\r\n")
        .await?;

    let mut root_dir = PathBuf::from(path);
    let mut cwd = root_dir.clone();
    let mut data_port = None; // 用于存储被动模式下的监听端口

    loop {
        let mut buf = vec![0; 512];
        match stream.read(&mut buf).await {
            Ok(0) => break, // 客户端关闭连接
            Ok(n) => {
                let request = String::from_utf8_lossy(&buf[..n]);
                println!("Received: {}", request.trim_end());

                if let Some(cmd) = parse_command(&request) {
                    match cmd {
                        Command::User(_) => {
                            stream
                                .write_all(b"331 User name okay, need password\r\n")
                                .await?;
                        }
                        Command::Pass(_) => {
                            stream.write_all(b"230 User logged in\r\n").await?;
                        }
                        Command::Pwd => {
                            let pwd_response = format!("257 \"{}\"\r\n", cwd.display());
                            stream.write_all(pwd_response.as_bytes()).await?;
                        }
                        Command::Pasv => {
                            // 这里只是简单地选择了一个端口，实际上应随机选择一个未使用的端口
                            let local_addr = stream.local_addr()?;
                            data_port = Some(local_addr.port() + 1);
                            stream
                                .write_all(
                                    format!(
                                        "227 Entering Passive Mode ({},{}).\r\n",
                                        local_addr.ip().to_string().replace(".", ","),
                                        data_port.unwrap()
                                    )
                                    .as_bytes(),
                                )
                                .await?;
                        }
                        Command::List => {
                            if let Some(port) = data_port {
                                let entries = fs::read_dir(&cwd)?
                                    .filter_map(|entry| entry.ok())
                                    .map(|entry| entry.file_name().to_string_lossy().into_owned())
                                    .collect::<Vec<String>>()
                                    .join("\r\n");
                                println!("ls files......{}", entries);

                                // 创建数据连接并发送文件列表
                                let listener =
                                    TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
                                let (mut data_stream, _) = listener.accept().unwrap();
                                data_stream.write_all(entries.as_bytes())?;
                                stream.write_all(b"226 Directory send OK.\r\n").await?;
                            } else {
                                stream.write_all(b"425 Use PASV first.\r\n").await?;
                            }
                        }
                        Command::Retr(filename) => {
                            let file_path = cwd.join(&filename);
                            if file_path.exists() && file_path.is_file() {
                                let content = fs::read(&file_path)?;
                                stream.write_all(b"150 Opening BINARY mode data connection for file\r\n").await?;
                                stream.flush().await?;
                                stream.write_all(&content).await?;
                                stream.flush().await?;
                                stream.write_all(b"\r\n226 Transfer complete\r\n").await?;
                            } else {
                                stream
                                    .write_all(b"550 File not found or is a directory\r\n")
                                    .await?;
                            }
                        }
                        Command::Cwd(path) => {
                            let new_cwd = cwd.join(&path);
                            if new_cwd.exists() && new_cwd.is_dir() {
                                cwd = new_cwd;
                                stream.write_all(b"250 CWD command successful\r\n").await?;
                            } else {
                                stream.write_all(b"550 Directory not found\r\n").await?;
                            }
                        }
                        Command::XMKD(dir) => {
                            println!("create dir..........");
                            let file_path = cwd.join(&dir);
                            //let is_exist = file_path.exists();
                            //println!("dir is exist? {}", is_exist);
                            if !file_path.exists() {
                                match fs::create_dir_all(&file_path) {
                                    Ok(_) => {
                                        if let Err(e) =
                                            stream.write_all(b"257 Directory created\r\n").await
                                        {
                                            eprintln!("Failed to send response: {}", e);
                                        } else {
                                            println!("Directory created and response sent.");
                                        }
                                    }
                                    Err(e) => {
                                        eprintln!("Failed to create directory: {}", e);
                                        if let Err(e) = stream
                                            .write_all(b"550 Failed to create directory\r\n")
                                            .await
                                        {
                                            eprintln!("Failed to send error response: {}", e);
                                        }
                                    }
                                }
                            } else {
                                if let Err(e) =
                                    stream.write_all(b"550 Directory already exists\r\n").await
                                {
                                    eprintln!("Failed to send response: {}", e);
                                }
                            }
                        }
                        Command::Quit => {
                            stream.write_all(b"221 Goodbye\r\n").await?;
                            break;
                        }
                    }
                } else {
                    stream.write_all(b"500 Unknown command\r\n").await?;
                }
            }
            Err(e) => {
                eprintln!("Error reading from socket: {:?}", e);
                break;
            }
        }
    }

    Ok(())
}

#[derive(Debug)]
enum Command {
    User(String),
    Pass(String),
    Pwd,
    Pasv,
    List,
    Retr(String),
    Cwd(String),
    XMKD(String),
    Quit,
}

fn parse_command(request: &str) -> Option<Command> {
    let parts: Vec<&str> = request.trim_end().split_whitespace().collect();
    match parts.get(0)?.to_uppercase().as_str() {
        "USER" => Some(Command::User(parts.get(1)?.to_string())),
        "PASS" => Some(Command::Pass(parts.get(1)?.to_string())),
        "PWD" | "XPWD" => Some(Command::Pwd),
        "PASV" => Some(Command::Pasv),
        "NLIST | LIST" | "NLST" => Some(Command::List),
        "RETR" => Some(Command::Retr(parts.get(1)?.to_string())),
        "CWD" => Some(Command::Cwd(parts.get(1)?.to_string())),
        "XMKD" => Some(Command::XMKD(parts.get(1)?.to_string())),
        "QUIT" => Some(Command::Quit),
        _ => None,
    }
}
